#include <common.h>
#include <pci.h>

#include <asm/io.h>

#include <mach/loongson.h>

DECLARE_GLOBAL_DATA_PTR;

#define LS2K_PCI_IO_MASK 0x1ffffff

#define PCI_EXP_DEVCTL_READRQ  0x7000	/* Max_Read_Request_Size */
#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0  /* Max_Payload_Size */

struct pci_config_data {
	int bus;
	int dev;
	int func;
	int interrupt;
	int primary;
	int secondary;
	int subordinate;
	unsigned int mem_start;
	unsigned int mem_end;
	unsigned int io_start;
	unsigned int io_end;
#define __PCI_DEV		0x1
#define __PCI_BRIDGE	0x2
	int type;
}__attribute__((aligned(4)));

static struct pci_config_data pci_config_array[] = {
				/*		APB		*/
	[0] = {
		.bus = 0, .dev = 0x2, .func = 0, .interrupt = 0, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x1fe00000, .mem_end = 0x1fe0ffff, .type = __PCI_DEV,
	},
				/*		GMAC0	*/
	[1] = {
		.bus = 0, .dev = 0x3, .func = 0, .interrupt = 20, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40040000, .mem_end = 0x4004ffff, .type = __PCI_DEV,
	},
				/*		GMAC1	*/
	[2] = {
		.bus = 0, .dev = 0x3, .func = 1, .interrupt = 22, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40050000, .mem_end = 0x4005ffff, .type = __PCI_DEV,
	},
				/*		OTG		*/
	[3] = {
		.bus = 0, .dev = 0x4, .func = 0, .interrupt = 57, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40000000, .mem_end = 0x4003ffff, .type = __PCI_DEV,
	},
				/*		EHCI	*/
	[4] = {
		.bus = 0, .dev = 0x4, .func = 1, .interrupt = 58, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40060000, .mem_end = 0x4006ffff, .type = __PCI_DEV,
	},
				/*		OHCI	*/
	[5] = {
		.bus = 0, .dev = 0x4, .func = 2, .interrupt = 59, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40070000, .mem_end = 0x4007ffff, .type = __PCI_DEV,
	},
				/*		GPU		*/
	[6] = {
		.bus = 0, .dev = 0x5, .func = 0, .interrupt = 37, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x40080000, .mem_end = 0x400bffff, .type = __PCI_DEV,
	},
				/*		DC		*/
	[7] = {
		.bus = 0, .dev = 0x6, .func = 0, .interrupt = 36, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x400c0000, .mem_end = 0x400cffff, .type = __PCI_DEV,
	},
				/*		HDA		*/
	[8] = {
		.bus = 0, .dev = 0x7, .func = 0, .interrupt = 12, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x400d0000, .mem_end = 0x400dffff, .type = __PCI_DEV,
	},
				/*		SATA	*/
	[9] = {
		.bus = 0, .dev = 0x8, .func = 0, .interrupt = 27, .primary = 0, .secondary = 0,
		.subordinate = 0, .mem_start = 0x400e0000, .mem_end = 0x400effff, .type = __PCI_DEV,
	},
};

static struct pci_config_data pci_port_config_array[] = {
				/*	PCIE0-PORT0	*/
	[0] = {
		.bus = 0, .dev = 0x9, .func = 0, .interrupt = 40, .primary = 0, .secondary = 1,
		.subordinate = 1, .mem_start = 0x40100000, .mem_end = 0x4fffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18000000, .io_end = 0x180fffff,
	},
				/*	PCIE0-PORT1	*/
	[1] = {
		.bus = 0, .dev = 0xa, .func = 0, .interrupt = 41, .primary = 0, .secondary = 4,
		.subordinate = 4, .mem_start = 0x50000000, .mem_end = 0x53ffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18100000, .io_end = 0x181fffff,
	},
				/*	PCIE0-PORT2	*/
	[2] = {
		.bus = 0, .dev = 0xb, .func = 0, .interrupt = 42, .primary = 0, .secondary = 8,
		.subordinate = 8, .mem_start = 0x54000000, .mem_end = 0x57ffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18200000, .io_end = 0x182fffff,
	},
				/*	PCIE0-PORT3	*/
	[3] = {
		.bus = 0, .dev = 0xc, .func = 0, .interrupt = 43, .primary = 0, .secondary = 0xc,
		.subordinate = 0xc, .mem_start = 0x58000000, .mem_end = 0x5fffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18300000, .io_end = 0x183fffff,
	},
				/*	PCIE1-PORT0	*/
	[4] = {
		.bus = 0, .dev = 0xd, .func = 0, .interrupt = 44, .primary = 0, .secondary = 0x10,
		.subordinate = 0x10, .mem_start = 0x60000000, .mem_end = 0x77ffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18400000, .io_end = 0x184fffff,
	},
				/*	PCIE1-PORT1	*/
	[5] = {
		.bus = 0, .dev = 0xe, .func = 0, .interrupt = 45, .primary = 0, .secondary = 0x14,
		.subordinate = 0x14, .mem_start = 0x78000000, .mem_end = 0x7fffffff, .type = __PCI_BRIDGE,
		.io_start = 0x18500000, .io_end = 0x185fffff,
	},
};

static void ls_pcie_mem_fixup(struct pci_config_data *pdata)
{
	pci_dev_t dev;
	u32 val;
	unsigned int io_start;
	unsigned int io_end;

	dev = PCI_BDF(pdata->bus, pdata->dev, pdata->func);
	pci_read_config_dword(dev, PCI_VENDOR_ID, &val);
	/*	device on the slot	*/
	if (val != 0xffffffff) {
		if (pdata->type == __PCI_DEV) {
			/*write bar*/
			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, pdata->mem_start);
		} else {
			pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x0);
			/*write memory base and memory limit*/
			val = ((pdata->mem_start >> 16) & 0xfff0) | (pdata->mem_end & 0xfff00000);
			pci_write_config_dword(dev, PCI_MEMORY_BASE, val);
			pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, val);

			io_start = pdata->io_start & LS2K_PCI_IO_MASK;
			io_end = pdata->io_end & LS2K_PCI_IO_MASK;
			/*write io upper 16bit base and io upper 16bit limit*/
			val = ((io_start >> 16) & 0xffff) | (io_end & 0xffff0000);
			pci_write_config_dword(dev, PCI_IO_BASE_UPPER16, val);
			/*write io base and io limit*/
			val = ((io_start >> 8) & 0xf0) | (io_end & 0xf000);
			val |= 0x1 | (0x1 << 8);
			pci_write_config_word(dev, PCI_IO_BASE, val);
		}
	}
}

static void ls_pcie_busnr_fixup(struct pci_config_data *pdata)
{
	pci_dev_t dev;
	u32 val;

	dev = PCI_BDF(pdata->bus, pdata->dev, pdata->func);
	pci_read_config_dword(dev, PCI_VENDOR_ID, &val);
	/*	device on the slot	*/
	if (val != 0xffffffff) {
		if (pdata->type == __PCI_BRIDGE) {
			/*write primary ,secondary and subordinate*/
			val = pdata->primary | (pdata->secondary << 8) | (pdata->subordinate << 16);
			pci_write_config_dword(dev, PCI_PRIMARY_BUS, val);
		}
	}
}
#if 0
static void ls_pcie_interrupt_fixup(struct pci_config_data *pdata)
{
	pci_dev_t dev;
	u32 val;

	dev = PCI_BDF(pdata->bus, pdata->dev, pdata->func);
	pci_read_config_dword(dev, PCI_VENDOR_ID, &val);
	/*	device on the slot	*/
	if (val != 0xffffffff)
		pci_write_config_word(dev, PCI_INTERRUPT_LINE, pdata->interrupt | 0x100);
}

static void ls_pcie_payload_fixup(struct pci_config_data *pdata)
{
	pci_dev_t dev;
	u32 val;
	u16 max_payload_spt, control;

	dev = PCI_BDF(pdata->bus, pdata->dev, pdata->func);
	pci_read_config_dword(dev, PCI_VENDOR_ID, &val);
	/*	device on the slot	*/
	if (val != 0xffffffff) {
		if (pdata->type == __PCI_BRIDGE) {
			/*set Max_Payload_Size & Max_Read_Request_Size*/
			max_payload_spt = 1;
			pci_read_config_word(dev, 0x78, &control);
			control &= (~PCI_EXP_DEVCTL_PAYLOAD & ~PCI_EXP_DEVCTL_READRQ);
			control |= ((max_payload_spt << 5) | (max_payload_spt << 12));
			pci_write_config_word(dev, 0x78, control);
		}
	}
}
#endif
static void ls_pci_msi_window_config(void)
{
	void __iomem *base;
	/*config msi window*/
//	writeq(0x000000001fe10000ULL, (void __iomem *)CKSEG1ADDR(0x1fe12500));
	base = ioremap(LS2X_XBAR_WIN5_BASE, 1);
	writel(0x1fe10000, base);
	writel(0x00000000, base + 4);
//	writeq(0xffffffffffff0000ULL, (void __iomem *)CKSEG1ADDR(0x1fe12540));
	base = ioremap(LS2X_XBAR_WIN5_MASK, 1);
	writel(0xffff0000, base);
	writel(0xffffffff, base + 4);
//	writeq(0x000000001fe10081ULL, (void __iomem *)CKSEG1ADDR(0x1fe12580));
	base = ioremap(LS2X_XBAR_WIN5_MMAP, 1);
	writel(0x1fe10081, base);
	writel(0x00000000, base + 4);
}

static void ls_dma_coherent(void)
{
	u32 val;
	void __iomem *dma_base = ioremap(LS2X_APB_DMA_BASE, 1);

	val = __raw_readl(dma_base);
	val |= 0x2; //apbdma0
	__raw_writel(val, dma_base);

	val = __raw_readl(dma_base + 0x10);
	val |= 0x2; //apbdma1
	__raw_writel(val, dma_base + 0x10);

	val = __raw_readl(dma_base + 0x20);
	val |= 0x2; //apbdma2
	__raw_writel(val, dma_base + 0x20);

	val = __raw_readl(dma_base + 0x30);
	val |= 0x2; //apbdma3
	__raw_writel(val, dma_base + 0x30);

	val = __raw_readl(dma_base + 0x40);
	val |= 0x2; //apbdma4
	__raw_writel(val, dma_base + 0x40);
}

static void ls_map_gpu_addr(void)
{
	void __iomem *cpu_win7_base = ioremap(LS2X_CPU_WIN0_BASE + 0x38, 1);
	void __iomem *cpu_win7_mask = ioremap(LS2X_CPU_WIN0_MASK + 0x38, 1);
	void __iomem *cpu_win7_mmap = ioremap(LS2X_CPU_WIN0_MMAP + 0x38, 1);

	if (gd->arch.memorysize_total == SZ_2G) {
		writel(0x20000000, cpu_win7_base);
		writel(0x00000000, cpu_win7_base + 4);
		writel(0xe0000000, cpu_win7_mask);
		writel(0xffffffff, cpu_win7_mask + 4);
		writel(0x600000f0, cpu_win7_mmap);
		writel(0x00000000, cpu_win7_mmap + 4);
	} else if (gd->arch.memorysize_total == SZ_4G) {
		writel(0x20000000, cpu_win7_base);
		writel(0x00000000, cpu_win7_base + 4);
		writel(0xe0000000, cpu_win7_mask);
		writel(0xffffffff, cpu_win7_mask + 4);
		writel(0xe00000f0, cpu_win7_mmap);
		writel(0x00000000, cpu_win7_mmap + 4);
	} else if (gd->arch.memorysize_total == SZ_8G) {
		writel(0x20000000, cpu_win7_base);
		writel(0x00000000, cpu_win7_base + 4);
		writel(0xe0000000, cpu_win7_mask);
		writel(0xffffffff, cpu_win7_mask + 4);
		writel(0xe00000f0, cpu_win7_mmap);
		writel(0x00000001, cpu_win7_mmap + 4);
	} else {
		printf("Now this Memory size %ld MB is not support mapping GPU address.\n", (long)(gd->arch.memorysize_total/1024/1024));
	}
}

void ls_pcie_fixup(int port_fix)
{
	int i;

	for (i=0; i<ARRAY_SIZE(pci_config_array); i++) {
		ls_pcie_mem_fixup(pci_config_array + i);
//		ls_pcie_interrupt_fixup(pci_config_array + i);
		ls_pcie_busnr_fixup(pci_config_array + i);
//		ls_pcie_payload_fixup(pci_config_array + i);
	}

	if (port_fix)
	{
		for (i=0; i<ARRAY_SIZE(pci_port_config_array); i++) {
			ls_pcie_mem_fixup(pci_port_config_array + i);
			ls_pcie_busnr_fixup(pci_port_config_array + i);
		}
	}

	ls_pci_msi_window_config();

	ls_dma_coherent();

	//读取cpuid判断2k1000的版本
#ifdef CONFIG_MIPS
	if ((read_c0_prid() & 0xffff) == 0x00006103)
#endif
	{
		u32 val;
		void __iomem *common_cfg2 = ioremap(LS2X_COMMON_CONF2, 1);
		//set dc coherent
		val = __raw_readl(common_cfg2);
		val |= 0x8;
		__raw_writel(val, common_cfg2);

		ls_map_gpu_addr();
	}
}
